﻿
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Windows.Markup;
using System.Xaml;
using System.Xaml.Schema;

using alib;
using alib.Array;
using alib.String;
using alib.Debugging;
using alib.Enumerable;
using alib.Reflection;

using agree.schema;
using agree.schema.xaml;

namespace agree
{
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public sealed class xaml_obj_writer_debug : xaml_obj_writer
	{
		public xaml_obj_writer_debug(XamlLoadInstance xli)
			: base(xli)
		{
		}

		public override void WriteNamespace(NamespaceDeclaration namespaceDeclaration)
		{
			base.WriteNamespace(namespaceDeclaration);
		}
		public override void WriteStartObject(XamlType xamlType)
		{
			base.WriteStartObject(xamlType);

			DebugPrint("<{0}>", xamlType.UnderlyingType._Name(false).Replace('<', '[').Replace('>', ']'));
			_indent_tags++;
		}
		public override void WriteEndObject()
		{
			var t = CurrentType.UnderlyingType;
			base.WriteEndObject();

			if (_indent_tags > 0)
				_indent_tags--;
			DebugPrint("</{0}>", t._Name(false).Replace('<', '[').Replace('>', ']'));
		}
		protected override void OnAfterBeginInit(Object value)
		{
			var to = value.GetType();
			DebugPrint("￫ created {0}.{1} {2:X8}", to.Namespace, to._Name(false), value.GetHashCode());

			base.OnAfterBeginInit(value);
		}
		protected override void OnAfterEndInit(Object value)
		{
			base.OnAfterEndInit(value);
		}
		protected override void OnBeforeProperties(Object value)
		{
			base.OnBeforeProperties(value);

			DebugPrint("￫ begin {0:X8}", value.GetHashCode());
		}
		protected override void OnAfterProperties(Object value)
		{
			DebugPrint("￫ end {0:X8}", value.GetHashCode());

			base.OnAfterProperties(value);
		}
		public override void WriteStartMember(XamlMember property)
		{
			DebugPrint("StartMember: .{0}", property.Name);
			_indent_extra++;

			base.WriteStartMember(property);
		}
		public override void WriteEndMember()
		{
			base.WriteEndMember();

			if (_indent_extra > 0)
				_indent_extra--;
			DebugPrint("EndMember");
		}

		public override void WriteGetObject()
		{
			_indent_extra--;
			DebugPrint("<{0}> (Get)", CurrentPropertyName);
			_indent_extra++;

			base.WriteGetObject();
		}
		protected override bool OnSetValue(Object o, XamlMember member, Object value)
		{
			var to = o.GetType();
			var tv = value.GetType();
			var tm = member.Type.UnderlyingType;
			if (!tm.IsAssignableFrom(tv))
			{
				var xtc = member.TypeConverter;
				TypeConverter tc = null;

				if (xtc == null || (tc = xtc.ConverterInstance) == null || !tc.CanConvertFrom(tv))
				{
					var msg = tc == null ?
								"No value converter to" :
								String.Format("Value converter '{0}' cannot", tc.GetType()._Name(false));

					msg += String.Format(
@" convert /{0}/ to /{1}/.

Target object: {2}({3})

Member: .{4}  /{5}/

Value to set: {6}({7})",
						tv._Name(false),
						tm._Name(false),

						get_name(o),
						to._Name(false),

						member.Name,
						member.TargetType.UnderlyingType._Name(false),

						get_name(value),
						get_info(value));

					throw new Exception(msg);
				}

				xtc.ConverterInstance.ConvertFrom(value);
			}
			DebugPrint("SetValue({0:X8}.{1} ({2}), {3})", o.GetHashCode(), member.Name, tm._Name(false), get_info(value));
			return base.OnSetValue(o, member, value);
		}
		public override void WriteValue(Object value)
		{
			base.WriteValue(value);
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// 
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

		const int IndentSize = 4;
		public int _indent_tags = 1;
		public int _indent_extra;
		bool f_need_return;

		String indent
		{
			[DebuggerStepThrough]
			get
			{
				int ii = (_indent_tags + _indent_extra) * IndentSize;
				return new String(' ', ii);
			}
		}
		[DebuggerStepThrough]
		String get_info(Object obj)
		{
			String s = "";
			if (obj is String)
				s += ((String)obj).DoubleQuote();
			else if (obj is IString)
				s += ((IString)obj).Text.DoubleQuote();
			else
			{
				s += obj.GetType()._Name(false);
			}
			var p = PP.Name;
			if (p != null)
			{
				String name = (String)PP.Name.GetValue(obj);
				if (name != null)
					s += "[" + name + "]";
			}
			return s;
		}
		[DebuggerStepThrough]
		String get_name(Object obj)
		{
			var p = PP.Name;
			if (p == null)
				return "(name not available)";
			String name = (String)PP.Name.GetValue(obj);
			return name != null ? " [" + name + "]" : "";
		}
		static String _clean(String s)
		{
			if (s == null)
				return "(null)";
			if (s.StartsWith("agree."))
				return s.Substring(6);
			if (s.StartsWith("System."))
				return s.Substring(7);
			return s;
		}
		[DebuggerStepThrough]
		T DebugPrint<T>(Func<T> f1, String fmt, params Object[] args)
		{
			return DebugPrint(f1, ts<T>, fmt, args);
		}
		[DebuggerStepThrough]
		static String ts<T>(T t) { return t.ToString(); }
		[DebuggerStepThrough]
		T DebugPrint<T>(Func<T> f1, Func<T, String> f2, String fmt, params Object[] args)
		{
			DebugWrite(fmt, args);
			f_need_return = true;
			_indent_extra++;
			T t = f1();
			_indent_extra--;
			String s = ": " + (t == null ? "(null)" : f2(t));
			if (f_need_return)
				f_need_return = false;
			else
				s = indent + "=> " + s;
			Debug.Write(s + Environment.NewLine);
			return t;
		}
		[DebuggerStepThrough]
		void DebugPrint(String fmt, params Object[] args)
		{
			DebugWrite(fmt + Environment.NewLine, args);
		}
		[DebuggerStepThrough]
		void DebugWrite(String fmt, params Object[] args)
		{
			if (f_need_return)
			{
				fmt = " ..." + Environment.NewLine + indent + fmt;
				f_need_return = false;
			}
			else
				fmt = indent + fmt;
			for (int i = 0; i < args.Length; i++)
			{
				Object o = args[i];
				if (o == null)
					continue;
				String s = o as String;
				//if (s == null && o is IString)
				//	s = ((IString)o).Text;
				if (s != null && s.Contains((Char)10))
					s = s.Where(ch => ch >= 32).Take(50).NewString() + "...";
				else
					s = _clean(o.ToString());
				args[i] = s;
			}
			Debug.Write(String.Format(fmt, args));
		}
	};
}
